/*
 * Copyright (c) 2006-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.livejournal;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.EventObject;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.text.JTextComponent;

import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI;
import org.panteleyev.blogclient.BlogPostEditorFrame;
import org.panteleyev.blogclient.BloggerOutboxFrame;
import org.panteleyev.blogclient.BloggingKit;
import org.panteleyev.blogclient.DateTimePicker;
import org.panteleyev.blogclient.FontManager;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.Options;
import org.panteleyev.blogclient.PreviewFrame;
import org.panteleyev.blogclient.TagListManager;
import org.panteleyev.blogclient.UI;
import org.panteleyev.blogclient.events.OutboxChangedEvent;
import org.panteleyev.blogclient.events.PostDeletedEvent;
import org.panteleyev.blogclient.events.TemplatesChangedEvent;
import org.panteleyev.html.Editor;
import org.panteleyev.html.PlainEditor;
import org.panteleyev.ljapi.LJAccount;
import org.panteleyev.ljapi.LJAdultContent;
import org.panteleyev.ljapi.LJEvent;
import org.panteleyev.ljapi.LJEventAccessLevel;
import org.panteleyev.ljapi.LJException;
import org.panteleyev.ljapi.LJFriend;
import org.panteleyev.ljapi.LJFriendAlphabeticalComparator;
import org.panteleyev.ljapi.LJHelpers;
import org.panteleyev.ljapi.LJMood;
import org.panteleyev.ljapi.LJProperties;
import org.panteleyev.ljapi.LJTag;
import org.panteleyev.ljapi.LJUserpic;
import org.panteleyev.music.Music;
import org.panteleyev.music.MusicTrackInfo;
import org.panteleyev.swing.CollapseButton;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SimpleComboBoxModel;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.utilities.events.AppEventListener;

/**
 *
 * @author Petr Panteleyev
 */
public class LJEditorFrame extends BlogPostEditorFrame<LJUserProfile,LJEvent> {
    private JTextField tagsEdit;
    private JButton tagsUpdateButton;
    private DateTimePicker datePicker;
    private JButton tagsButton;
    private JCheckBox manualDateCheck;
    private JLabel userpicShowLabel;
    private JComboBox userpicDropDown;
    private JTextField subjectEdit;
    private JCheckBox postToManyCheckBox;
    private JComboBox sharedJournalsComboBox;
    private JComboBox moodDropDown;
    private JTextField musicEdit;
    private JLabel locationLabel;
    private JTextComponent locationEdit;
    private JComboBox accessComboBox;
    private JComboBox screenComboBox;
    private JLabel adultContentLabel;
    private JComboBox adultContentComboBox;
    private JCheckBox noAutoFormatCheck;
    private JCheckBox noCommentsCheck;
    private JCheckBox noEmailCheck;
    private JCheckBox backdateCheck;
    private JXCollapsiblePane collPane;
    private JLabel idStatusField;
    private JLabel fileNameField;
    private JTextPane plainEditor = new JTextPane();

    private class ReloadTagsThread extends SwingWorker<Object,Object> {
        private Exception ex;
        private ArrayList<LJTag> tags;

        ReloadTagsThread() {
            startLongOperation();
        }

        @Override
        protected Object doInBackground() throws Exception {
            try {
                tags = m_account.loadUserTags(null);
            } catch (IOException | LJException e) {
                ex = e;
            }

            return null;
        }

        @Override
        protected void done() {
            if (ex != null) {
                handleException(ex);
            } else {
                if (tags != null) {
                    getProfile().storeUserTags(tags);
                    tagsMenu = tagListManager.buildTagsMenu(m_account.getUserTags());
                }
            }

            finishLongOperation();
        }
    }

    private LJAccount m_account;
    private DefaultComboBoxModel userpicDropDownModel;
    private SimpleComboBoxModel moodDropDownModel;
    private LJEvent m_post;
    private File _outboxFile;

    private AbstractAction  securityLevelAction;
    private AbstractAction  openTagsListAction;
    private AbstractAction  detectMusicAction;
    private AbstractAction  insertAdvUserReferenceAction;
    private AbstractAction  insertRepostAction;
    private AbstractAction  insertLikeAction;
    /* LJ-Cut */
    private AbstractAction ljcutOpenAction;
    private AbstractAction ljcutWithTextAction;
    private AbstractAction ljcutCloseAction;
    private AbstractAction ljcutSurroundAction;

    private static String[] securityTypes;
    private static String[] screenTypes;
    private static String[] adultContentTypes;
    private boolean internalAccessChange;

    private AdvancedUserReferenceDialog m_userRefDialog;

    private File m_saveAsFile;

    private PlainEditor editor = new PlainEditor();
    private TagListManager          tagListManager;

    /* Shared global resources */
    private JMenu friendsMenu = new JMenu();
    private JMenu communitiesMenu = new JMenu();
    private JMenu tagsMenu = new JMenu();
    private JXStatusBar statusBar = new JXStatusBar();

    final void constructor(LJAccount account, LJEvent post, File file) {
        this.m_account = account;
        _outboxFile = file;

//        m_post = (post == null)? new LJEvent() : post;

        createActions();
//        ljcutPopupMenu = createLJCutMenu();
        initComponents();
        createMenuBar();
        createFriendMenu();

        plainEditor.setFont(FontManager.getEditorFont());

        tagListManager = new TagListManager(tagsEdit);
        tagsMenu = tagListManager.buildTagsMenu(m_account.getUserTags());

        editor.setEditor(plainEditor);
        editor.newDocument();

        datePicker.setEnabled(false);

        createListModels();

        setPost(post);

        /* Hide not supported controls */
        locationLabel.setVisible(this.m_account.isEventLocationPropertySupported());
        locationEdit.setVisible(this.m_account.isEventLocationPropertySupported());
        tagsUpdateButton.setVisible(this.m_account.isTagsLoadModeSupported());
        adultContentLabel.setVisible(this.m_account.isAdultContentSupported());
        adultContentComboBox.setVisible(this.m_account.isAdultContentSupported());

        /* We cannot update tags in offline mode */
        tagsUpdateButton.setEnabled(getProfile().isOnline());
        getPublishAndContinueAction().setEnabled(getProfile().isOnline());

        /* Setup undo/redo */
        updateUndoRedo(false, false);
        getTextUndoManager().addComponent(plainEditor);
        getTextUndoManager().addComponent(subjectEdit);
        getTextUndoManager().addComponent(musicEdit);
        getTextUndoManager().addComponent(locationEdit);
        getTextUndoManager().addComponent(tagsEdit);

        addAppEventListener(TemplatesChangedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                plainEditor.setComponentPopupMenu(generateEditorPopupMenu());
            }
        });

        addAppEventListener(FriendListUpdatedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                createFriendMenu();
            }
        });

        Options.getBounds(this);
    }

    private void initComponents() {
        getContentPane().setLayout(new BorderLayout());

        JPanel centerPanel = new JPanel(new BorderLayout());
        getContentPane().add(centerPanel, BorderLayout.CENTER);

        getContentPane().add(SwingFactory.createToolBarPanel(
            createProfileToolBar(false, true),
            Box.createHorizontalGlue(),
            getBusyLabel(),
            Box.createHorizontalStrut(5)
        ), BorderLayout.NORTH);

        // Subject panel
        centerPanel.add(SwingFactory.createPanel(BorderFactory.createEmptyBorder(5, 5, 5, 5), SwingFactory.LAYOUT.BOX_X,
            new JLabel(getPostWindowSubjectLabelText()),
            Box.createHorizontalStrut(3),
            subjectEdit = new JTextField(),
            Box.createHorizontalStrut(5),
            sharedJournalsComboBox = new JComboBox(),
            Box.createHorizontalStrut(3),
            postToManyCheckBox = new JCheckBox(new AbstractAction(L10N.EDITOR_FRAME_TO_MANY_JOURNALS_CHECKBOX.s()) {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    sharedJournalsComboBox.setEnabled(!postToManyCheckBox.isSelected());
                }
            })
        ), BorderLayout.NORTH);

        sharedJournalsComboBox.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                m_post.setJournal((String)sharedJournalsComboBox.getSelectedItem());
            }
        });

        subjectEdit.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusLost(java.awt.event.FocusEvent evt) {
                setTitle();
            }
        });

        // Editor and scroller
        JScrollPane scroll = new JScrollPane(plainEditor);
        scroll.setPreferredSize(new Dimension(1, 1));
        plainEditor.setComponentPopupMenu(generateEditorPopupMenu());
        centerPanel.add(scroll, BorderLayout.CENTER);

        JPanel optionsPanelTop = SwingFactory.createPanel(BorderFactory.createEmptyBorder(0, 5, 5, 5),
            SwingFactory.LAYOUT.BOX_X,
            new JLabel(L10N.EDITOR_FRAME_TAGS_LABEL.s()),
            Box.createHorizontalStrut(3),
            tagsEdit = new JTextField(),
            Box.createHorizontalStrut(3),
            tagsButton = new JButton(openTagsListAction),
            Box.createHorizontalStrut(3),
            tagsUpdateButton = new JButton(getUpdateTagsAction()),
            new JSeparator(JSeparator.VERTICAL),
            manualDateCheck = new JCheckBox(),
            Box.createHorizontalStrut(3),
            datePicker = new DateTimePicker()
        );

        manualDateCheck.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                datePicker.setEnabled(manualDateCheck.isSelected());
            }
        });

        JPanel attrPanel;
        JPanel secPanel;
        JPanel upicPanel;

        JPanel optionsPanel = SwingFactory.createPanel(BorderFactory.createEmptyBorder(5, 5, 5, 5),
            SwingFactory.LAYOUT.BOX_X,
            // Attributes
            attrPanel = SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                new JLabel(L10N.EDITOR_FRAME_MOOD_LABEL.s()),
                moodDropDown = new JComboBox(),
                Box.createVerticalStrut(3),
                new JLabel(L10N.EDITOR_FRAME_MUSIC_LABEL.s()),
                SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_X,
                    musicEdit = new JTextField(),
                    Box.createHorizontalStrut(3),
                    new JButton(detectMusicAction)
                ),
                Box.createVerticalStrut(3),
                locationLabel = new JLabel(L10N.EDITOR_FRAME_LOCATION_LABEL.s()),
                locationEdit = new JTextField(),
                Box.createVerticalGlue()
            ),
            Box.createHorizontalStrut(5),
            new JSeparator(JSeparator.VERTICAL),
            Box.createHorizontalStrut(5),
            secPanel = SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                new JLabel(L10N.EDITOR_FRAME_ACCESS_LEVEL_LABEL.s()),
                SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_X,
                    accessComboBox = new JComboBox(new DefaultComboBoxModel(securityTypes)),
                    Box.createHorizontalStrut(3),
                    new JButton(securityLevelAction)
                ),
                Box.createVerticalStrut(3),
                new JLabel(L10N.EDITOR_FRAME_SCREEN_COMMENTS_LABEL.s()),
                screenComboBox = new JComboBox(new DefaultComboBoxModel(screenTypes)),
                Box.createVerticalStrut(3),
                adultContentLabel = new JLabel(L10N.EDITOR_FRAME_ADULT_CONTENT_LABEL.s()),
                adultContentComboBox = new JComboBox(new DefaultComboBoxModel(adultContentTypes)),
                Box.createVerticalGlue()
            ),
            Box.createHorizontalStrut(5),
            new JSeparator(JSeparator.VERTICAL),
            Box.createHorizontalStrut(5),
            SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                noAutoFormatCheck = new JCheckBox(new AbstractAction(L10N.EDITOR_FRAME_NO_AUTOFORMAT_CHECKBOX.s()) {
                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        m_post.setNoAutoFormat(noAutoFormatCheck.isSelected());
                    }
                }),
                Box.createVerticalStrut(3),
                noCommentsCheck = new JCheckBox(new AbstractAction(L10N.EDITOR_FRAME_NO_COMMENTS_CHECKBOX.s()) {
                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        m_post.setNoComments(noCommentsCheck.isSelected());
                    }
                }),
                Box.createVerticalStrut(3),
                noEmailCheck = new JCheckBox(new AbstractAction(L10N.EDITOR_FRAME_NO_EMAIL_CHECKBOX.s()) {
                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        m_post.setNoEmail(noEmailCheck.isSelected());
                    }
                }),
                Box.createVerticalStrut(3),
                backdateCheck = new JCheckBox(new AbstractAction(L10N.EDITOR_FRAME_BACKDATE_CHECKBOX.s()) {
                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        m_post.setBackdate(backdateCheck.isSelected());
                    }
                })
            ),
            Box.createHorizontalStrut(5),
            new JSeparator(JSeparator.VERTICAL),
            Box.createHorizontalStrut(5),
            upicPanel = SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                userpicShowLabel = new JLabel(),
                Box.createVerticalStrut(3),
                userpicDropDown = new JComboBox(userpicDropDownModel = new SimpleComboBoxModel(new Vector(m_account.getUserpicKeys()))),
                Box.createVerticalGlue()
            )
        );

        accessComboBox.setRenderer(new AccessLevelListCellRenderer());

        collPane = new JXCollapsiblePane();
        collPane.setLayout(new BoxLayout(collPane.getContentPane(), BoxLayout.Y_AXIS));
        SwingFactory.add(collPane,
            new JSeparator(),
            optionsPanel
        );
        collPane.setCollapsed(true);

        JButton tButton = new CollapseButton(collPane);
        tButton.setFocusable(false);

        JPanel optionsPanelMain = SwingFactory.createPanel(BorderFactory.createEmptyBorder(5, 0, 0, 5), SwingFactory.LAYOUT.BOX_X,
            SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                optionsPanelTop,
                collPane
            ),
            tButton
        );

        centerPanel.add(optionsPanelMain, BorderLayout.SOUTH);

        moodDropDown.setEditable(true);
        userpicShowLabel.setPreferredSize(new Dimension(102, 102));
        SwingFactory.setHeightMaximumToPreferred(secPanel.getComponents());
        SwingFactory.setHeightMaximumToPreferred(attrPanel.getComponents());
        SwingFactory.setHeightMaximumToPreferred(userpicDropDown);
        SwingFactory.setAlignmentX(attrPanel, LEFT_ALIGNMENT);
        SwingFactory.setAlignmentX(secPanel, LEFT_ALIGNMENT);
        SwingFactory.setAlignmentX(upicPanel, CENTER_ALIGNMENT);
        SwingFactory.setAlignmentY(optionsPanel, TOP_ALIGNMENT);
        SwingFactory.setAlignmentY(optionsPanelMain, TOP_ALIGNMENT);

        accessComboBox.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if (internalAccessChange) {
                    return;
                }

                int index = accessComboBox.getSelectedIndex();
                m_post.setAccessLevel(LJEventAccessLevel.getValue(index));
                if (index == 3) {
                    onEditAccessLevel();
                }
            }
        });

        screenComboBox.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                m_post.setScreeningType(screenComboBox.getSelectedIndex());
            }
        });

        adultContentComboBox.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                m_post.setAdultContent(LJAdultContent.getValue(adultContentComboBox.getSelectedIndex()));
            }
        });

        userpicDropDown.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                int index = userpicDropDown.getSelectedIndex();
                if (index != -1) {
                    LJUserpic upic = m_account.getUserpic(index);
                    ImageIcon icon = getProfile().getUserpicImage(upic);
                    userpicShowLabel.setIcon(icon);
                }
            }
        });

        plainEditor.setMargin(UI.EDITOR_MARGIN);

        // Status Bar
        statusBar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);
        JXStatusBar.Constraint c1 = new JXStatusBar.Constraint(JXStatusBar.Constraint.ResizeBehavior.FILL);
        statusBar.add(idStatusField = new JLabel());
        statusBar.add(fileNameField = new JLabel(), c1);
        getContentPane().add(statusBar, BorderLayout.SOUTH);

        // OS X styling
        if (OSUtil.isMacOSX()) {
            sharedJournalsComboBox.putClientProperty("JComboBox.isPopDown", Boolean.TRUE);
            accessComboBox.putClientProperty("JComboBox.isPopDown", Boolean.TRUE);
            screenComboBox.putClientProperty("JComboBox.isPopDown", Boolean.TRUE);
            adultContentComboBox.putClientProperty("JComboBox.isPopDown", Boolean.TRUE);
            userpicDropDown.putClientProperty("JComboBox.isPopDown", Boolean.TRUE);
        }
    }

    @Override
    public void setVisible(boolean b) {
        super.setVisible(b);
        plainEditor.requestFocus();
    }

    @Override
    protected void setEmptyPost() {
        setPost(null);
    }

    private void setPost(LJEvent post) {
        m_post = (post == null)? new LJEvent() : post;

        if ((post == null) || (post.getUserpic() == null)) {
            userpicDropDown.getAction().actionPerformed(null);
        } else {
            this.userpicDropDown.setSelectedItem(post.getUserpic());
        }

        setupAccessLevelComboBox();

        subjectEdit.setText(m_post.getSubject());
        plainEditor.setText(m_post.getBody());
        tagsEdit.setText(m_post.getTagList());
        musicEdit.setText(m_post.getMusic());
        locationEdit.setText(m_post.getLocation());

        moodDropDown.setSelectedIndex(m_account.getMoodIndex(m_post.getMoodID()));
        if (m_post.getMoodText() != null) {
            moodDropDown.setSelectedItem(m_post.getMoodText());
        }

        noCommentsCheck.setSelected(m_post.getNoComments());
        noAutoFormatCheck.setSelected(m_post.getNoAutoFormat());
        noEmailCheck.setSelected(m_post.getNoEmail());
        backdateCheck.setSelected(m_post.getBackdate());

        datePicker.setDate(m_post.getDate());
        if (post != null) {
            manualDateCheck.setSelected(true);
            datePicker.setEnabled(true);
        }

        sharedJournalsComboBox.setModel(new DefaultComboBoxModel(m_account.getSharedJournals().toArray()));
        if (post != null) {
            sharedJournalsComboBox.setSelectedItem(post.getJournal());
        }

        idStatusField.setText(m_post.getId());
        statusBar.validate();

        if (!m_post.getId().equals("-1")) {
            sharedJournalsComboBox.setEnabled(false);
            postToManyCheckBox.setEnabled(false);
        } else {
            postToManyCheckBox.setEnabled(_outboxFile == null);
            getDeleteEntryAction().setEnabled(_outboxFile != null);
            setIconImage(Icon.NEW_POST.i().getImage());
        }

        screenComboBox.setSelectedIndex(m_post.getScreeningType());
        adultContentComboBox.setSelectedIndex(m_post.getAdultContent().ordinal());
        // TODO: The rest!!

        setTitle();
    }

    public LJEditorFrame(LJUserProfile profile, LJEvent post) {
        super(profile);
        constructor(profile.getAccount(), post, null);
    }

    public LJEditorFrame(LJUserProfile profile, LJEvent post, File file) {
        super(profile);
        constructor(profile.getAccount(), post, file);
    }

    private void createActions() {
        /* Editor actions */

        insertAdvUserReferenceAction = new AbstractAction(L10N.EDITOR_FRAME_INSERT_ADV_USER_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onInsertAdvancedUserReference();
            }
        };
        insertAdvUserReferenceAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.ALT_MASK | BloggingKit.getKeyMask()));
        insertAdvUserReferenceAction.putValue(Action.SHORT_DESCRIPTION, L10N.EDITOR_FRAME_INSERT_ADV_USER_ACTION.s());

        insertRepostAction = new AbstractAction(L10N.INSERT_REPOST_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                onInsertRepost();
            }
        };
        insertRepostAction.putValue(Action.SHORT_DESCRIPTION, "Insert Repost...");

        insertLikeAction = new AbstractAction("Like!...", Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                onInsertLike();
            }
        };

        /* Internal controls actions */
        securityLevelAction = new AbstractAction(null, Icon.LOCK_NICE.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onEditAccessLevel();
            }
        };

        openTagsListAction = new AbstractAction(null, Icon.FORWARD.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onOpenTagsList();
            }
        };

        detectMusicAction = new AbstractAction(null, Icon.MUSIC.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onDetectMusic();
            }
        };

        ljcutOpenAction = new AbstractAction("LJ Cut") {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onLJCutOpen(null);
            }
        };

        ljcutWithTextAction = new AbstractAction(L10N.LJCUT_MENU_WITH_TEXT.s()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                String initText = getSelectedText();
                String text = JOptionPane.showInputDialog(LJEditorFrame.this, "LJ-Cut:", initText);
                if (text != null) {
                    onLJCutOpen(text);
                }
            }
        };
        ljcutWithTextAction.putValue(Action.SHORT_DESCRIPTION, L10N.LJCUT_MENU_WITH_TEXT.s());

        ljcutCloseAction = new AbstractAction(L10N.LJCUT_MENU_CLOSE.s()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onLJCutClose();
            }
        };
        ljcutCloseAction.putValue(Action.SHORT_DESCRIPTION, L10N.LJCUT_MENU_CLOSE.s());

        ljcutSurroundAction = new AbstractAction(L10N.LJCUT_MENU_SURROUND.s()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onLJCutSurround();
            }
        };
    }

    private void createMenuBar() {
        createMenuBar(
            createFileMenu(),
            createEditMenu(
                getUndoAction(),
                getRedoAction(),
                null,
                getCutAction(),
                getCopyAction(),
                getPasteAction(),
                null,
                getEditInsertReferenceAction(),
                getEditInsertImageAction(),
                insertAdvUserReferenceAction,
                insertRepostAction,
                insertLikeAction,
                createLJCutMenu(),
                null,
                generateQuotesSubmenu(),
                generateSymbolsSubmenu(),
                getTemplateMenu(),
                null,
                getEditTypografAction()
            ),
            generateFormatMenu(),
            createEntryMenu(),
            createProfileMenu(),
            createToolsMenu(),
            createWindowMenu(),
            createHelpMenu()
        );
    }

    private JMenu createLJCutMenu() {
        JMenu menu = new JMenu("LJ Cut");
        menu.add(ljcutOpenAction);
        menu.add(ljcutWithTextAction);
        menu.add(ljcutCloseAction);
        menu.add(ljcutSurroundAction);
        return menu;
    }

    private void setTitle() {
        final StringBuilder b = new StringBuilder();

        final String subjText = subjectEdit.getText();
        if (subjText.length() != 0) {
            b.append(subjText);
        } else {
            final String t = (m_post.getId().equals("-1"))? getPostWindowNewPostTitle() : getPostWindowEditPostTitle();
            b.append(t);
        }

        setTitle(b.toString());
    }

    private void createListModels() {
        moodDropDownModel = new SimpleComboBoxModel() {
            @Override
            public int getSize() { return m_account.getMoods().size(); }
            @Override
            public Object getElementAt(int index) { return m_account.getMoods().get(index).getName(); }
        };
        moodDropDown.setModel(moodDropDownModel);


    }

    public String getPostID() { return (m_post == null)? "-1" : m_post.getId(); }
    @Override
    public LJEvent getPost() { return m_post; }
    public String getUseJournal() { return (m_post == null)? null : m_post.getJournal(); }

    private void onDetectMusic() {
        new SwingWorker<Object,Object>() {
            private MusicTrackInfo info;
            @Override
            protected Object doInBackground() throws Exception {
                info = Music.getCurrentTrack();
                return null;
            }

            @Override
            protected void done() {
                if (info != null) {
                    musicEdit.setText(info.toString());
                } else {
                    musicEdit.setText("");
                }
            }
        }.execute();
    }

    @Override
    public void onUpdateTags() {
        new ReloadTagsThread().execute();
    }

    private void onFriendsMenuItemSelected(String name) {
        onInsertFriend(name);
    }

    private void onCommunityMenuItemSelected(String name) {
        onInsertCommunity(name);
    }

    private void onOpenTagsList() {
        if (tagsMenu != null) {
            int x = this.tagsButton.getX();
            int y = this.tagsButton.getY();
            tagsMenu.getPopupMenu().show(this.tagsButton.getParent(), x, y);
        }
    }

    @Override
    protected LJEvent createPostObject() {
        LJEvent post = new LJEvent();

        post.setSubject(subjectEdit.getText());
        post.setBody(plainEditor.getText());
        post.setMusic(musicEdit.getText());
        post.setLocation(locationEdit.getText());

        if (datePicker.isEnabled()) {
            post.setDate(datePicker.getDate());
        } else {
            post.setDate(null);
        }

        int selIndex = this.userpicDropDown.getSelectedIndex();
        if (selIndex > 0) {
            post.setUserpic(m_account.getUserpic(selIndex).getKey());
        } else {
            post.setUserpic(null);
        }

        int moodIndex = this.moodDropDown.getSelectedIndex();
        String moodText = (String)this.moodDropDown.getSelectedItem();
        int moodID = -1;
        if (moodIndex != -1) {
            LJMood mood = this.m_account.getMoods().get(moodIndex);
            if (mood != null) {
                moodID = mood.getID();
            }
        }
        post.setMood(moodID, moodText);

        post.setJournal((String)this.sharedJournalsComboBox.getSelectedItem());

        String tagList = tagsEdit.getText().trim();
        post.setTagList(tagList);

        return post;
    }

    private void setEventObject() {
        m_post.setSubject(subjectEdit.getText());
        m_post.setBody(plainEditor.getText());
        m_post.setMusic(musicEdit.getText());
        m_post.setLocation(locationEdit.getText());

        if (datePicker.isEnabled()) {
            m_post.setDate(datePicker.getDate());
        } else {
            m_post.setDate(null);
        }

        int selIndex = this.userpicDropDown.getSelectedIndex();
        if (selIndex > 0) {
            m_post.setUserpic(m_account.getUserpic(selIndex).getKey());
        } else {
            m_post.setUserpic(null);
        }

        int moodIndex = this.moodDropDown.getSelectedIndex();
        String moodText = (String)this.moodDropDown.getSelectedItem();
        int moodID = -1;
        if (moodIndex != -1) {
            LJMood mood = this.m_account.getMoods().get(moodIndex);
            if (mood != null) {
                moodID = mood.getID();
            }
        }
        m_post.setMood(moodID, moodText);

        m_post.setJournal((String)this.sharedJournalsComboBox.getSelectedItem());

        String tagList = tagsEdit.getText().trim();
        m_post.setTagList(tagList);
    }

    private void setupAccessLevelComboBox() {
        internalAccessChange = true;
        accessComboBox.setSelectedIndex(m_post.getAccessLevel().ordinal());
        internalAccessChange = false;
    }

    private void onEditAccessLevel() {
        EventAccessLevelDialog dial = new EventAccessLevelDialog(this, m_post, m_account);
        if (dial.showDialog()) {
            setupAccessLevelComboBox();
        }
    }

    private void onPost(boolean close, String[] journals) {
        setEventObject();

        if (journals == null) {
            journals = new String[] { m_post.getJournal() };
        }

        try {
            for (String j : journals) {
                if (j != null) {
                    m_post.setJournal(j);
                    this.m_account.postEvent(m_post);
                    if (close) {
                        /* this is needed for another posting
                         * when posting to multiple journals the window is always closed
                         */
                        m_post.setID(-1);
                    }
                }
            }

            /* Remove outbox file if exists and we are online */
            if (getProfile().isOnline() && (_outboxFile != null)) {
                _outboxFile.delete();
                _outboxFile = null;
                getProfile().sendApplicationEvent(new OutboxChangedEvent(this));
            }

            String tagList = tagsEdit.getText().trim();
            if (tagList.length() != 0) {
                this.m_account.addLocalTags(tagList);
                getProfile().storeUserTags(this.m_account.getUserTags());
                tagsMenu = tagListManager.buildTagsMenu(m_account.getUserTags());
            }

            if (close) {
                afterPostSent();
            }
        } catch (IOException | LJException e) {
            handleException(e);
        }
    }

    @Override
    public void insertText(String text) {
        getEditor().insertText(text);
    }

    private void onInsertRepost() {
        String excerpt = null;

        LJRepostDialog dial = new LJRepostDialog(this, excerpt);
        if (dial.showDialog()) {
            String text = LJHelpers.buildRepostHTML(dial.getButtonText(), dial.getExcerpt());
            insertText(text);
        }
    }

    private void onInsertLike() {
        LJLikeDialog dial = new LJLikeDialog(this);
        if (dial.showDialog()) {
            final StringBuilder b = new StringBuilder("<lj-like ");
            final String str = dial.getOptionsString();
            if (str != null && !str.isEmpty()) {
                b.append("buttons=\"")
                    .append(str)
                    .append("\"");
            }
            b.append(" />");
            insertText(b.toString());
        }
    }

    public void onInsertFriend(String username) {
        if ((m_userRefDialog != null) && (m_userRefDialog.isVisible())) {
            m_userRefDialog.userMenuItemSelected(username);
        } else {
            if (username == null) {
                username = JOptionPane.showInputDialog(this, "Friend");
            }

            if (username != null) {
                StringBuilder b = new StringBuilder("<lj user=\"");
                b.append(username);
                b.append("\">");

                LJProperties props = m_account.getProperties();
                insertText(String.format(props.getLJUser(), username));
            }
        }
    }

    public void onInsertCommunity(String username) {
        if ((m_userRefDialog != null) && (m_userRefDialog.isVisible())) {
            m_userRefDialog.userMenuItemSelected(username);
        } else {
            if (username == null) {
                username = JOptionPane.showInputDialog(this, "Community");
            }

            if (username != null) {
                StringBuilder b = new StringBuilder("<lj comm=\"");
                b.append(username);
                b.append("\">");

                insertText(b.toString());
            }
        }
    }


    public void onLJCutOpen(String text) {
        StringBuilder b = new StringBuilder("<lj-cut");

        if (text == null) {
            String sel = plainEditor.getSelectedText();
            text = sel;
        }

        if (text != null) {
            b.append(" text=\"");
            b.append(text);
            b.append("\"");
        }
        b.append(">");

        getEditor().insertText(b.toString());
    }

    public void onLJCutClose() {
        getEditor().insertText("</lj-cut>");
    }

    public void onLJCutSurround() {
        String sel = plainEditor.getSelectedText();
        StringBuilder b = new StringBuilder("<lj-cut>");
        if (sel != null) {
            b.append(sel);
        }
        b.append("</lj-cut>");
        getEditor().insertText(b.toString());
    }

    @Override
    public String getSelectedText() {
        return editor.getSelectedText();
    }

    public JMenu getCommunitiesMenu() {
        return communitiesMenu;
    }

    public JMenu getFriendsMenu() {
        return friendsMenu;
    }

    public void onInsertAdvancedUserReference() {
        if (m_userRefDialog == null) {
            m_userRefDialog = new AdvancedUserReferenceDialog(this, m_account);
        }

        if (m_userRefDialog.showDialog()) {
            getEditor().insertText(m_userRefDialog.getReference());
        }
    }

    /*
    public void onSave() {
        if (m_saveAsFile == null) {
            onSaveAs();
        } else {
            try {
                setEventObject();
                XMLDocument doc = XMLDocument.newDocument();

                m_post.store(doc);
                XMLHelpers.export(doc, m_saveAsFile);
            }
            catch (Exception e) {
                handleException(e);
            }
        }
    }

    public void onSaveAs() {
        setEventObject();

        try {
            XMLDocument doc = XMLDocument.newDocument();

            m_post.store(doc);

            JFileChooser dial = new JFileChooser();
            dial.addChoosableFileFilter(new XMLFileFilter());
            dial.setAcceptAllFileFilterUsed(false);
            if (dial.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
                javax.swing.filechooser.FileFilter filter = dial.getFileFilter();
                File file = dial.getSelectedFile();
                if ((filter != null) && (filter instanceof FileFilter)) {
                    file = ((FileFilter)filter).appendExtension(file);
                }

                m_saveAsFile = file;
                doc.export(m_saveAsFile);
                fileNameField.setText(m_saveAsFile.getAbsolutePath());
            }
        } catch (Exception e) {
            handleException(e);
        }
    }
     * */

    static {
        securityTypes = new String[] {
            L10N.LJ_SECURITY_PUBLIC.s(),
            L10N.LJ_SECURITY_PRIVATE.s(),
            L10N.LJ_SECURITY_FRIENDS.s(),
            L10N.LJ_SECURITY_CUSTOM.s()
        };

        screenTypes = new String[] {
            L10N.LJ_SCREEN_DEFAULT.s(),
            L10N.LJ_SCREEN_NO.s(),
            L10N.LJ_SCREEN_ANONYMOUS.s(),
            L10N.LJ_SCREEN_NON_FRIENDS.s(),
            L10N.LJ_SCREEN_ALL.s(),
        };

        adultContentTypes = new String[] {
            L10N.LJ_ADULT_CONTENT_DEFAULT.s(),
            L10N.LJ_ADULT_CONTENT_NONE.s(),
            L10N.LJ_ADULT_CONTENT_CONCEPTS.s(),
            L10N.LJ_ADULT_CONTENT_EXPLICIT.s()
        };
    }

    @Override
    public Editor getEditor() {
        return editor;
    }

    public void createFriendMenu() {
        friendsMenu.removeAll();
        communitiesMenu.removeAll();

        final Iterable<LJFriend> friends = m_account.getFriends(new LJFriendAlphabeticalComparator());

        String[] lastLtr = new String[] {"", ""};
        JMenu[] menu = new JMenu[2];
        JMenuItem[] lastItem = new JMenuItem[2];
        JMenu targetMenu;

        for (LJFriend f : friends) {
            int index;

            JMenuItem item = new JMenuItem(f.getUserName());

            if (f.isCommunity()) {
                index = 1;
                item.setIcon(Icon.COMMUNITY_ICON.i());
                item.addActionListener(new java.awt.event.ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        onCommunityMenuItemSelected(e.getActionCommand());
                    }
                });
                targetMenu = communitiesMenu;
            } else {
                index = 0;
                item.setIcon(Icon.FRIEND_HEAD.i());
                item.addActionListener(new java.awt.event.ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        onFriendsMenuItemSelected(e.getActionCommand());
                    }
                });
                targetMenu = friendsMenu;
            }


            String ltr = f.getUserName().substring(0, 1);

            if (ltr.equals(lastLtr[index])) {
                if (lastItem[index] != null) {
                    menu[index] = new JMenu(ltr);
                    targetMenu.remove(lastItem[index]);
                    targetMenu.add(menu[index]);
                    menu[index].add(lastItem[index]);
                    lastItem[index] = null;
                }

                menu[index].add(item);

            } else {
                lastItem[index] = item;
                lastLtr[index] = ltr;
                targetMenu.add(item);
            }
        }

        friendsMenu.setEnabled(true);
        communitiesMenu.setEnabled(true);
    }

    @Override
    public void onPublish() {
        if (postToManyCheckBox.isEnabled() && postToManyCheckBox.isSelected()) {
            SharedJournalSelectionDialog dial = new SharedJournalSelectionDialog(this, m_account.getSharedJournals());
            if (dial.showDialog()) {
                onPost(true, dial.getSelectedJournals());
            }
        } else {
            onPost(true, null);
        }
    }

    @Override
    public void onPublishAndContinue() {
        if (postToManyCheckBox.isEnabled() && postToManyCheckBox.isSelected()) {
            SharedJournalSelectionDialog dial = new SharedJournalSelectionDialog(this, m_account.getSharedJournals());
            if (dial.showDialog()) {
                onPost(false, dial.getSelectedJournals());
            }
        } else {
            onPost(false, null);
        }
    }

    @Override
    public void onPublishLater() {
        String[] journals = null;

        if (postToManyCheckBox.isEnabled() && postToManyCheckBox.isSelected()) {
            SharedJournalSelectionDialog dial = new SharedJournalSelectionDialog(this, m_account.getSharedJournals());
            if (dial.showDialog()) {
                journals = dial.getSelectedJournals();
            }
        }

        setEventObject();

        if (m_post.getDate() == null) {
            m_post.setDate(new Date());
        }

        if (journals == null) {
            journals = new String[] { m_post.getJournal() };
        }

        try {
            for (String j : journals) {
           //     if (j != null) {
                    m_post.setJournal(j);

                    /* File name */
                    File saveFile = null;
                    if (_outboxFile == null) {
                        String fName = BloggerOutboxFrame.getOutboxFileName();
                        saveFile = new File(getProfile().getOutboxDirectory(), fName);
                    } else {
                        saveFile = _outboxFile;
                    }
                    save(m_post, saveFile);
                    getProfile().sendApplicationEvent(new OutboxChangedEvent(this));

                    /* this is needed for another posting
                     * when posting to multiple journals the window is always closed
                     */
                    m_post.setID(-1);
           //     }
            }

            String tagList = tagsEdit.getText().trim();
            if (tagList.length() != 0) {
                this.m_account.addLocalTags(tagList);
                getProfile().storeUserTags(this.m_account.getUserTags());
                tagsMenu = tagListManager.buildTagsMenu(m_account.getUserTags());
            }

            afterPostSent();
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void onPreviewEntry() {
        m_post.setSubject(subjectEdit.getText());
        m_post.setBody(plainEditor.getText());
        int selIndex = this.userpicDropDown.getSelectedIndex();
        if (selIndex > 0) {
            m_post.setUserpic(m_account.getUserpic(selIndex).getKey());
        } else {
            m_post.setUserpic(null);
        }
        if (datePicker.isEnabled()) {
            m_post.setDate(datePicker.getDate());
        } else {
            m_post.setDate(new Date());
        }

        try {
            Previewer previewer = new Previewer(m_account, m_post);

            if (Options.getUseExternalPreview()) {
                OSUtil.runBrowser(previewer.getCutFileURL().toString());
            } else {
                LJPreviewFrame pFrame = (LJPreviewFrame)PreviewFrame.findPreviewFrame(LJPreviewFrame.class, this);
                if (pFrame == null) {
                    pFrame = new LJPreviewFrame(getProfile(), this);
                }
                pFrame.setContent(previewer.getCutFileURL(), previewer.getFullFileURL());
                pFrame.setVisible(true);
            }
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void onDeleteEntry() {
        if (MainClass.showConfirmDialog(this)) {
            try {
                if (_outboxFile != null) {
                    _outboxFile.delete();
                    _outboxFile = null;
                    getProfile().sendApplicationEvent(new OutboxChangedEvent(this));
                } else {
                    this.m_account.deleteEvent(m_post);
                    getProfile().sendApplicationEvent(new PostDeletedEvent(this, m_post));
                }

                afterPostSent();
            } catch (IOException e) {
                handleException(e);
            } catch (LJException e) {
                handleException(e);
            }
        }
    }

}
